#ifndef _H_CAMERA
#define _H_CAMERA
#include <mutex>
#include "heads.hpp"
#include "optics.hpp"
#include "object.hpp"
#include "film.h"
namespace chen {


extern unsigned __raycnt;
// Render the image pixel by pixel



class Camera: public Object {
public:
	Film * film;
	Camera(Film * f, const Transform * w2c):
		Object::Object(w2c), film(f) {}
	// 对相机进行采样
	virtual Ray rayFromPixel (Float w, Float h, Float * pdf) const = 0;
	virtual RayDifferential rayDifferentialFromCamera (Float w, Float h, Float * pdf) const = 0;
	inline void submit (const Sample * samples, int nsamples) const {
		film->submit(samples, nsamples);
	}
};

class SampleHolder {
	constexpr static int buffer_size = 8192;
	Sample buffer[buffer_size];
	const Camera * camera;
public:
	int current_sample;
	inline SampleHolder (const Camera * cam): camera(cam), current_sample(0) {}
	inline ~SampleHolder () {
		if(current_sample) camera->submit(buffer, current_sample);
	}
	inline void push (const Sample & s) {
		buffer[current_sample++] = s;
		if(current_sample == buffer_size) {
			camera->submit(buffer, buffer_size);
			current_sample = 0;
		}
	}
	inline void finish () {
		if(current_sample) camera->submit(buffer, current_sample);
	}
	
};

class BasicCamera: public Camera {
public:
	Float fov, t_max;
	inline BasicCamera (Film * f, const Transform * w2c, Float fov, Float t_max):
		Camera::Camera(f, w2c), fov(fov), t_max(t_max) {

		}
	inline Ray rayFromPixel (Float w, Float h, Float * pdf) const {
		Float swh = tan(fov), shh = swh*(film->resolution_h/film->resolution_w);
		Ray r;
		r.o = Point3(0);
		r.d = Vector3(2*swh*w/film->resolution_w-swh, 2*shh*h/film->resolution_h-shh, -1).normalized();
		r.t = t_max;
		if(pdf != nullptr) *pdf = 1;
		r = w2o->reversed(r);
		//printf("%f %f %f %f %f %f\n", r.o.x, r.o.y, r.o.z, r.d.x, r.d.y, r.d.z);
		//getchar();
		return r;
	}
	inline RayDifferential rayDifferentialFromCamera (Float w, Float h, Float * pdf) const {
		RayDifferential rd = rayFromPixel(w, h, pdf);
		Ray rx = rayFromPixel(w+1, h, nullptr);
		Ray ry = rayFromPixel(w, h+1, nullptr);
		rd.dxori = rx.o;
		rd.dxdir = rx.d;
		rd.dyori = ry.o;
		rd.dydir = ry.d;
		rd.differentials = true;
		return rd;
	}
};


}
#endif
